package clueless.utilities;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;

import clueless.server.Player;

public class Message {

	//////////////////////
	// Client message tags
	//////////////////////
	public static final String m_JOIN_GAME           = "Join Game";
	public static final String m_MOVE                = "Move";
	public static final String m_TAKE_SECRET_PASSAGE = "Take Secret Passage";
	public static final String m_SUGGEST             = "Suggest";
	public static final String m_ACCUSE              = "Accuse";
	public static final String m_REFUTE              = "Refute";
	
	public static final String m_GET_REFUTE          = "Get Refute";
	
	//////////////////////
	// Server message tags
	//////////////////////
	//public static final String m_PLAYER_JOINED               = "Player Joined";
	//public static final String m_START_GAME                  = "Start Game";
	public static final String m_CHARACTER_START_ROOMS       = "Character Start Rooms";
	public static final String m_PLAYER_CARDS                = "Player Cards";
	public static final String m_START_TURN                  = "Start Turn";
	public static final String m_PLAYER_MOVES                = "Player Moves";
	public static final String m_PLAYER_TAKES_SECRET_PASSAGE = "Player Takes Secret Passage";
	public static final String m_PLAYER_SUGGESTS             = "Player Suggests";
	public static final String m_PLAYER_REFUTES              = "Player Refutes";
	public static final String m_PLAYER_CANNOT_REFUTE        = "Player CannotRefute";
	public static final String m_ALL_PLAYERS_CANNOT_REFUTE   = "Players CannotRefute";
	public static final String m_PLAYER_ACCUSES              = "Player Accuses";
	//public static final String m_PLAYER_WINS                 = "Player Wins";
	public static final String m_PLAYER_LOSES                = "Player Loses";
	public static final String m_DISPLAY_TEXT                = "Display Text";
	public static final String m_GAME_OVER                   = "Game Over";

	/////////////////////
	// Message attributes
	/////////////////////
	public static final String m_DESIRED_NUM_PLAYERS   = "Desired Number of Players";
	//public static final String m_NUM_PLAYERS_REMAINING = "Number of Players Remaining";
	//public static final String m_PLAYER_NUM          = "Player Number";
	public static final String m_SENDER_ID             = "Sender ID";
	public static final String m_CHARACTER             = "Character";
	public static final String m_WEAPON                = "Weapon";
	public static final String m_ROOM                  = "Room";
	public static final String m_TEXT                  = "Text";
	public static final String m_MOVEMENT              = "Movement";
	
	////////////////
	// Miscellaneous
	////////////////
	public static final String m_VALUE_NOT_FOUND = "Value Not Found";
	public static final String m_SERVER          = "Server";
	public static final String m_SEPARATOR       = ":";
	
	//////////////////////////////
	// General messaging functions
	//////////////////////////////
	public static void sendMessage(PrintWriter outputStream, String message) {

		outputStream.println(message);
		outputStream.flush();

	}
	
	public static String getMessage(BufferedReader inputStream) {

		String message = "";

		try {
			while((message = inputStream.readLine()) == null) {}
		}
		catch (IOException ex) {
			ex.printStackTrace();
		}

		return message;
	}

	public static String assembleMessage(String messageType, ArrayList<StringPair> messageInfo) {

		String message = messageType;
		if (messageInfo != null) {
			for (int i = 0; i < messageInfo.size(); i++) {
				StringPair info = messageInfo.get(i);
				message = message + m_SEPARATOR + info.getAttribute() + m_SEPARATOR + info.getValue();
			}
		}
		return message;
	}
	
	public static String getMessageType(String message) {

		String[] subStrings = message.split(m_SEPARATOR);

		return subStrings[0];

	}
	
	public static ArrayList<StringPair> getMessageInfo(String message) {

		String[] subStrings = message.split(m_SEPARATOR);

		ArrayList<StringPair> messageInfo = new ArrayList<StringPair>();
		for (int i = 0; i < (subStrings.length - 1)/2; i++) {
			messageInfo.add(new StringPair(subStrings[2*i + 1], subStrings[2*i + 2]));
		}

		return messageInfo;
	}
	
	public static boolean isAttributeFound(String attribute, String message) {

		ArrayList<StringPair> messageInfo = getMessageInfo(message);
		
		for (int i = 0; i < messageInfo.size(); i++) {
			StringPair info = messageInfo.get(i);
			if (attribute.equals(info.getAttribute())) return true;
		}
		
		return false;

	}
	
	public static String getValue(String attribute, String message) {

		ArrayList<StringPair> messageInfo = getMessageInfo(message);

		for (int i = 0; i < messageInfo.size(); i++) {
			StringPair info = messageInfo.get(i);
			if (attribute.equals(info.getAttribute())) return info.getValue();
		}
		
		return m_VALUE_NOT_FOUND;
	}
	
	public static String getMessageSender(String message) {

		if (isAttributeFound(m_SENDER_ID, message)) {
			return getValue(m_SENDER_ID, message);
		}

		return m_SERVER;
		
	}
	
	///////////////////////
	// Client-sent messages
	///////////////////////
	public static void sendJoinGameMessage
	(
		PrintWriter outputStream,
		int         desiredNumPlayers,
		String      desiredCharacter
	) {

		ArrayList<StringPair> messageInfo = new ArrayList<StringPair>();
		messageInfo.add(new StringPair(m_DESIRED_NUM_PLAYERS, Integer.toString(desiredNumPlayers)));
		messageInfo.add(new StringPair(m_CHARACTER          , desiredCharacter                   ));

		sendMessage(outputStream, assembleMessage(m_JOIN_GAME, messageInfo));
	}
	
	public static void sendAccustaionMessage(PrintWriter outputStream, String character, String weapon, String room) {
		ArrayList<StringPair> messageInfo = new ArrayList<StringPair>();
		messageInfo.add(new StringPair(m_CHARACTER, character));
		messageInfo.add(new StringPair(m_WEAPON, weapon));
		messageInfo.add(new StringPair(m_ROOM, room));
		
		sendMessage(outputStream, assembleMessage(m_PLAYER_ACCUSES, messageInfo));
	}
	

	///////////////////////
	// Server-sent messages
	///////////////////////
	public static void sendMessageToOnePlayer(Player player, String message) {
		
		sendMessage(player.getConnection().getOutputStream(), message);

	}
	
	public static void sendMessageToAllPlayers(ArrayList<Player> allPlayers, String message) {
		
		for (int i = 0; i < allPlayers.size(); i++) {
			sendMessageToOnePlayer(allPlayers.get(i), message);
		}

	}
	
	public static void sendMessageToAllButOnePlayer
	(
		ArrayList<Player> allPlayers,
		Player            excludedPlayer,
		String            message
	) {
		
		for (int i = 0; i < allPlayers.size(); i++) {
			if (!(allPlayers.get(i).getCharacter().equals(excludedPlayer.getCharacter()))) {
				sendMessageToOnePlayer(allPlayers.get(i), message);
			}
		}

	}
	
	public static void sendTextMessageToOnePlayer(Player player, String textMessage) {
		
		ArrayList<StringPair> messageInfo = new ArrayList<StringPair>();
		messageInfo.add(new StringPair(m_TEXT, textMessage));
		sendMessageToOnePlayer(player, assembleMessage(m_DISPLAY_TEXT, messageInfo));
		
	}
	
	public static void sendTextMessageToAllPlayers(ArrayList<Player> allPlayers, String textMessage) {
		
		for(int i = 0; i < allPlayers.size(); i++) {
			sendTextMessageToOnePlayer(allPlayers.get(i), textMessage);
		}
		
	}
	
	public static void sendTextMessageToAllButOnePlayer
	(
		ArrayList<Player> allPlayers,
		Player            excludedPlayer,
		String            textMessage
	) {
		
		for(int i = 0; i < allPlayers.size(); i++) {
			if (!(allPlayers.get(i).getCharacter().equals(excludedPlayer.getCharacter()))) {
				sendTextMessageToOnePlayer(allPlayers.get(i), textMessage);
			}
		}
	
	}
	
	public static void sendPlayerJoinedMessage
	(
		ArrayList<Player> allPlayers,
		Player            newPlayer,
		int               numRemainingPlayers
	) {

		sendTextMessageToOnePlayer(newPlayer, "You have been added to a game as " + newPlayer.getCharacter());
		sendTextMessageToAllButOnePlayer(allPlayers, newPlayer, "A new player has been added as "
		                                                        + newPlayer.getCharacter());

		if (numRemainingPlayers > 0) {
			sendTextMessageToAllPlayers(allPlayers, "Still waiting for " + numRemainingPlayers + ((numRemainingPlayers==1)?(" player."):(" players.")));
		}
	}

	public static void sendStartGameMessage(ArrayList<Player> allPlayers) {

		sendTextMessageToAllPlayers(allPlayers, "All players have now joined and the game will now begin");
		sendTextMessageToAllPlayers(allPlayers, " ");
		sendTextMessageToAllPlayers(allPlayers, "Welcome to Clue-Less!  Let's play!");

		ArrayList<StringPair> messageInfo = new ArrayList<StringPair>();
		for(int i = 0; i < allPlayers.size(); i++) {
			messageInfo.add(new StringPair(m_CHARACTER, allPlayers.get(i).getCharacter()));
			messageInfo.add(new StringPair(m_ROOM     , allPlayers.get(i).getRoom().getMyRoom()));
		}

		sendMessageToAllPlayers(allPlayers, assembleMessage(m_CHARACTER_START_ROOMS, messageInfo));		
	}

	public static void sendPlayerCardsMessage(Player player, String[] cards) {
		//String[] splitCards = player.getCards().split(",");

		ArrayList<StringPair> cardList = new ArrayList<StringPair>();
		for (int i = 0; i < cards.length; i++) {
			if      (Card.isCharacter(cards[i])) cardList.add(new StringPair(Message.m_CHARACTER, cards[i]));
			else if (Card.isWeapon   (cards[i])) cardList.add(new StringPair(Message.m_WEAPON   , cards[i]));
			else                                 cardList.add(new StringPair(Message.m_ROOM     , cards[i]));
		}

		sendMessageToOnePlayer(player, assembleMessage(m_PLAYER_CARDS, cardList));
	}
	
	public static void sendPlayerWinsByDefaultMessage
	(
		ArrayList<Player> allPlayers,
		Player winningPlayer,
		Suggestion solution
	) {

		sendTextMessageToAllPlayers(allPlayers, "Congratulations!  The winner is " + winningPlayer.getCharacter());
		sendTextMessageToAllPlayers(allPlayers, "The solutions was " + solution.getCharacter() + " with the "
												+ solution.getWeapon() + " in the " + solution.getRoom());
		
		ArrayList<StringPair> messageInfo = new ArrayList<StringPair>();
		messageInfo.add(new StringPair(m_TEXT, "Congratulations!  The winner is " + winningPlayer.getCharacter()));
		sendMessageToAllPlayers(allPlayers, assembleMessage(m_GAME_OVER, messageInfo));		
	}

	public static void sendStartTurnMessage(ArrayList<Player> allPlayers, Player currentPlayer) {
		
		sendTextMessageToAllPlayers(allPlayers, "Waiting for " + currentPlayer.getCharacter());
		
		sendMessageToOnePlayer(currentPlayer, assembleMessage(m_START_TURN, null));

		//ArrayList<StringPair> messageInfo = new ArrayList<StringPair>();
		//messageInfo.add(new StringPair(m_CHARACTER, currentPlayer.getCharacter()));
		//sendMessageToOnePlayer(currentPlayer, assembleMessage(m_START_TURN, messageInfo));

		
		
	
	}

	public static void sendRequestSuggestionMessage(Player player) {		
		sendMessageToOnePlayer(player, assembleMessage(m_PLAYER_SUGGESTS, null));
	}

	// Inform next player that they need to refute suggestion 
	public static void sendGetRefute(Player nextActivePlayer,
			Suggestion suggestion) 
	{ 
		ArrayList<StringPair> messageInfo = new ArrayList<StringPair>();
		messageInfo.add(new StringPair(m_CHARACTER, suggestion.getCharacter()));
		messageInfo.add(new StringPair(m_WEAPON, suggestion.getWeapon()));
		messageInfo.add(new StringPair(m_ROOM, suggestion.getRoom()));
		
		sendMessageToOnePlayer(nextActivePlayer, assembleMessage(m_GET_REFUTE, messageInfo));
		
	}

	// Inform all players of player movement
	public static void sendPlayerMovement(ArrayList<Player> players, String character, String room) {
		
		ArrayList<StringPair> messageInfo = new ArrayList<StringPair>();
		messageInfo.add(new StringPair(m_CHARACTER, character));
		messageInfo.add(new StringPair(m_ROOM, room));
		
		sendMessageToAllPlayers(players, assembleMessage(m_PLAYER_MOVES, messageInfo));	
	}

	// Inform current player of a refute to a suggestion
	public static void sendRefuteToPlayer(Player currentPlayer, String refuteMessage) { 
		sendMessageToOnePlayer(currentPlayer, refuteMessage);
		
	}

	public static void sendRequestAccusation(Player currentPlayer) {
		sendMessageToOnePlayer(currentPlayer, assembleMessage(m_ALL_PLAYERS_CANNOT_REFUTE, null));
	}

	public static void sendMoveMessage(PrintWriter outputStream, String motion) {
		ArrayList<StringPair> messageInfo = new ArrayList<StringPair>();
		messageInfo.add(new StringPair(m_MOVEMENT, motion));
		sendMessage(outputStream, assembleMessage(m_PLAYER_MOVES, messageInfo));	
	}

	public static void sendAccuseMessage(PrintWriter outputStream,
			Suggestion accusation) {
		ArrayList<StringPair> messageInfo = new ArrayList<StringPair>();
		messageInfo.add(new StringPair(m_CHARACTER, accusation.getCharacter()));
		messageInfo.add(new StringPair(m_WEAPON, accusation.getWeapon()));
		messageInfo.add(new StringPair(m_ROOM, accusation.getRoom()));
		
		sendMessage(outputStream, assembleMessage(m_PLAYER_ACCUSES, messageInfo));
	}

	public static void sendSuggestMessage(PrintWriter outputStream,
			Suggestion suggestion) {
	
		ArrayList<StringPair> messageInfo = new ArrayList<StringPair>();
		messageInfo.add(new StringPair(m_CHARACTER, suggestion.getCharacter()));
		messageInfo.add(new StringPair(m_WEAPON, suggestion.getWeapon()));
		messageInfo.add(new StringPair(m_ROOM, suggestion.getRoom()));
		sendMessage(outputStream, assembleMessage(m_PLAYER_SUGGESTS, messageInfo));
		
	}

	public static void sendCannotRefuteMessage(PrintWriter outputStream) {
		sendMessage(outputStream, assembleMessage(m_PLAYER_CANNOT_REFUTE, null));
	}

	public static void sendRefuteMessage(PrintWriter outputStream, String refute) {
		ArrayList<StringPair> messageInfo = new ArrayList<StringPair>();
		messageInfo.add(new StringPair(m_TEXT, refute));
		sendMessage(outputStream, assembleMessage(m_PLAYER_REFUTES, messageInfo));
	}

	public static void sendAllPlayersCannotRefute(Player currentPlayer) {
		sendMessageToOnePlayer(currentPlayer, assembleMessage(m_ALL_PLAYERS_CANNOT_REFUTE, null));
		
	}

	
}
